package net.http;

import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;


import net.callback.FileUploadObserver;
import net.callback.OnRequestListener;
import net.client.HttpClient;
import net.exception.ApiException;
import net.exception.ExceptionEngine;
import net.interceptor.HttpHeaders;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;

/**
 * Created by FireMor on 2018/5/9.
 */

public class BaseRequest {
    private String TAG = "retrofitError";
    private final Retrofit.Builder rBuilder;
    private String baseUrl;
    private String mCacheFile = null;
    private int mTimeOut;             //请求超时
    protected int retryCount;//尝试次数
    private boolean mEnableLog = true;
    protected HttpHeaders headers = new HttpHeaders();                     //添加的header 暂无用到
    private  Retrofit retrofit; //d单例中旧的retrofit

    private volatile boolean hasSpecialSeting = false;
    private HttpClient httpClient = null;
    private Map<String, String> mHeadMap;
    private int currentRetryCount=0;
    private int maxConnectCount=10;//最大重连次数
    private long waitRetryTime;//等待时间
    public BaseRequest() {
        getInitialData();
        httpClient = new HttpClient();
        rBuilder = new Retrofit.Builder();
        rBuilder.baseUrl(baseUrl)
                .addConverterFactory(ScalarsConverterFactory.create())
                //如果请求返回额是json则使用下面的解析器，
                //注意：这两句的顺序不能对调，否则不能同时兼容字符串和json
                .addConverterFactory(GsonConverterFactory.create(buildGson()))
                //添加Rxjava的支持，把Retrofit转成Rxjava可用的适配类
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create());
    }

    /**
     * 提炼初始化值
     */
    private void getInitialData(){
        HttpManager httpManager = HttpManager.getInstance();
        baseUrl = httpManager.getBaseUrl();
        mTimeOut = httpManager.getmHttpClient().getTIME_OUT();
        mCacheFile = httpManager.getmHttpClient().getCacheDir();
        mEnableLog = httpManager.getmHttpClient().isEnableLog();
        mHeadMap = httpManager.getmHttpClient().getHeadMap();
        retrofit = httpManager.getRetrofit();
    }

    /**
     * 获取指定的Apiservice
     * 并在此处给出固定的refrofit
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getApiService(Class<T> clazz) {

        return getRetrofit().create(clazz);
    }

    /**
     *
     * @return 如果没有特殊设置，返回单例中的retrofit
     *
     * 否则，重新配置retrofit
     */
    private  Retrofit getRetrofit() {
        if (hasSpecialSeting){
            return rBuilder
                    .client(httpClient.buildClient())
                    .build();
        }
        return retrofit;
    }


    /**
     * 仅仅改变本请求的 baseURl
     * 加了过期，希望别有事没事就调用
     * @return
     */
    @Deprecated
    public BaseRequest localBaseUrl(String baseUrl){
        hasSpecialSeting = true;
        rBuilder.baseUrl(baseUrl);
        return this;
    }


    /**
     * 文件上传专用方法，可以监听上传进度
     *
     * @param observable
     * @param fileUploadObserver
     * @param <T>
     */
    public <T> void uploadFile(Observable<T> observable, FileUploadObserver<T> fileUploadObserver) {
        observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(@NonNull Disposable disposable) throws Exception {
                        //可以处理网络状态

                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(fileUploadObserver);
    }
    /**
     * 网络请求对外暴露的方法，Rxjava请求都走这个方法
     *
     * @param observable
     * @param observer
     * @param <T>
     */
    public <T> void subscriber(Observable<T> observable, Observer<T> observer) {
        observable.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(Observable<Throwable> throwable) throws Exception {
// 输出异常信息
                Log.d(TAG,  "发生异常 = "+ throwable.toString());
                /**
                 * 需求1：根据异常类型选择是否重试
                 * 即，当发生的异常 = 网络异常 = IO异常 才选择重试
                 */
                if (throwable instanceof Observable){

                    Log.d(TAG,  "属于IO异常，需重试" );

                    /**
                     * 需求2：限制重试次数
                     * 即，当已重试次数 < 设置的重试次数，才选择重试
                     */
                    if (currentRetryCount < maxConnectCount){
                        // 记录重试次数
                        currentRetryCount++;
                        Log.d(TAG,  "重试次数 = " + currentRetryCount);

                        /**
                         * 需求2：实现重试
                         * 通过返回的Observable发送的事件 = Next事件，从而使得retryWhen（）重订阅，最终实现重试功能
                         *
                         * 需求3：延迟1段时间再重试
                         * 采用delay操作符 = 延迟一段时间发送，以实现重试间隔设置
                         *
                         * 需求4：遇到的异常越多，时间越长
                         * 在delay操作符的等待时间内设置 = 每重试1次，增多延迟重试时间1s
                         */
                        // 设置等待时间
                        waitRetryTime = 19000 + currentRetryCount* 1000;
                        Log.d(TAG,  "等待时间 =" + waitRetryTime);
                        return Observable.just(1).delay(waitRetryTime, TimeUnit.MILLISECONDS);
                    }else{
                        // 若重试次数已 > 设置重试次数，则不重试
                        // 通过发送error来停止重试（可在观察者的onError（）中获取信息）
                        return Observable.error(new Throwable("重试次数已超过设置次数 = " +currentRetryCount  + "，即 不再重试"));
                    }
                }

                // 若发生的异常不属于I/O异常，则不重试
                // 通过返回的Observable发送的事件 = Error事件 实现（可在观察者的onError（）中获取信息）
                else{
                    return Observable.error(new Throwable("发生了非网络异常（非I/O异常）"));
                }            }
        })
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(@NonNull Disposable disposable) throws Exception {

                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

    public <T> Disposable subscriber(final Observable<T> observable, final OnRequestListener<T> listener) {
        final Disposable disposable = observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(@NonNull Disposable disposable) throws Exception {
                        //可以处理网络状态

                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<T>() {
                               @Override
                               public void accept(T t) throws Exception {
                                   listener.onComplete();
                                   listener.onSuccess(t);
                               }
                           },
                        new Consumer<Throwable>() {
                            @Override
                            public void accept(Throwable throwable) throws Exception {
                                listener.onComplete();
                                ApiException ex = ExceptionEngine.handleException(throwable);
                                listener.onFaild(ex.getCode(), ex.getMsg());
                            }
                        }, new Action() {
                            @Override
                            public void run() throws Exception {
                                listener.onComplete();
                            }
                        });

        disposable.dispose();
        return disposable;
    }
    /**
     * 普通的使用 请求，返回的是Call
     *
     * @param call
     * @param listener
     * @param <T>
     */
    public <T> void request(Call<T> call, final OnRequestListener<T> listener) {
        call.enqueue(new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, Response<T> response) {
                listener.onComplete();
                call.cancel();
                if (response.code() == 200) {
                    if (response.body() != null) {
                        listener.onSuccess(response.body());
                    } else {
                        listener.onFaild(2000, "出错了");
                    }
                } else {
                    ApiException exception = new ApiException(response.code());
                    listener.onFaild(exception.getCode(), exception.getMsg());
                }

            }

            @Override
            public void onFailure(Call<T> call, Throwable t) {
                listener.onComplete();
                call.cancel();
                try {
                    ApiException exception = ExceptionEngine.handleException(t);
                    listener.onFaild(exception.getCode(), exception.getMsg());
                } catch (Exception e1) {
                    e1.printStackTrace();
                    listener.onFaild(2000, "未知异常");
                }

            }
        });
    }
    public Gson buildGson() {
        return new GsonBuilder().setLenient()// json宽松
                .enableComplexMapKeySerialization()//支持Map的key为复杂对象的形式
                .serializeNulls() //智能null,支持输出值为null的属性
                .setPrettyPrinting()//格式化输出（序列化）
                .create();
    }


    /**
     * 设置http请求的缓存文件路劲，默认为空，可以不用设置。
     *
     * @param filePath
     * @return
     */
    public BaseRequest setCacheFile(String filePath) {
        mCacheFile = filePath;
        buildHttpClient();
        return this;
    }


    /**
     * 是否开启日志
     *
     * @param enable
     * @return
     */
    public BaseRequest enableLog(boolean enable) {
        mEnableLog = enable;
        buildHttpClient();
        return this;
    }

    /**
     * 设置请求超时时间
     *
     * @param time
     * @return
     */
    public BaseRequest setTimeOut(int time) {
        mTimeOut = time;
        buildHttpClient();
        return this;
    }

    /**
     * 设置附加的头信息，key为头名称，value为对应的值，默认已经设置了请求
     * 所需要的基本头，如果还需要额外的请求头，则可以通过这个方法进行设置
     * 此方法是针对某一个请求的
     *
     * @param head
     * @return
     */
    public BaseRequest setLocalHeadMap(Map<String, String> head) {
        mHeadMap = head;
        buildHttpClient();
        return this;
    }

    /**
     * 设置请求参数的参数
     */
    private void buildHttpClient(){
        hasSpecialSeting = true;
        httpClient.setTimeOut(mTimeOut);
        httpClient.setCacheDir(mCacheFile);
        httpClient.setHeadMap(mHeadMap);
        httpClient.enableLog(mEnableLog);
    }



}
